#include "VulkanBase.hpp"
#include "Core/Buffer/VertexBuffer.hpp"
#include "Core/Buffer/IndexBuffer.hpp"
#include "Core/Buffer/UniformBuffer.hpp"
#include "Scene/Mesh.hpp"
#include "Core/Texture/ColorTexture.hpp"
#include "Core/Texture/TextureManager.hpp"
#include "Core/Buffer/BufferManager.hpp"

void Lumine::VulkanBase::createWindow()
{
    if (!glfwInit())
    {
        std::cout << std::format("[ InitializeWindow ] ERROR\nFailed to initialize GLFW!\n");
        glfwTerminate();
    }
    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
    glfwWindowHint(GLFW_RESIZABLE, true);
    m_window = glfwCreateWindow(WIDTH, HEIGHT, "Lumine Vulkan", nullptr, nullptr);
    m_monitor = glfwGetPrimaryMonitor();
    glfwSetWindowUserPointer(m_window, this);
    glfwSetFramebufferSizeCallback(m_window,framebufferResizeCallback);
    if (!m_window)
    {
        std::cout << std::format("[ InitializeWindow ]\nFailed to create a glfw window!\n");
        glfwTerminate();
    }
}

void Lumine::VulkanBase::initVulkan()
{
    // 初始化全局上下文
    LumineContext::Get().InitializeInstance(m_window);
    LumineContext::Get().InitializeDevice();

    // 初始化核心功能组件
    m_swapchain.Initialize(m_window,nullptr);
    createVertex();
    createIndex();
    createUniform();
    createTexture();
    createDepthResources();
    vk::raii::Pipeline pipeline = m_pipeline.GetPipeline(m_swapchain.ImageFormat());
    m_renderer.Initialize(m_swapchain, m_pipeline);
    m_renderer.setPipeline(std::move(pipeline));
    std::cout << "Vulkan initialization complete." << std::endl;
}

void Lumine::VulkanBase::cleanup()
{
    BufferManager::uniformBuffers.cleanUp();
    glfwDestroyWindow(m_window);
    glfwTerminate();
}

void Lumine::VulkanBase::createVertex()
{
    VertexBuffer vertexBuffer;
    vertexBuffer.Create(sizeof(square[0]) * square.size());
    BufferManager::vertexBuffers = std::move(vertexBuffer);
}

void Lumine::VulkanBase::createIndex()
{
    IndexBuffer indexBuffer;
    indexBuffer.Create(sizeof(squareIndices[0]) * squareIndices.size());
    BufferManager::indexBuffers = std::move(indexBuffer);
}

void Lumine::VulkanBase::createTexture()
{
    ColorTexture texture;
    texture.Create();
    TextureManager::textures.emplace_back(std::move(texture));
}

void Lumine::VulkanBase::createDepthResources()
{
    DepthTexture depthTexture;
    depthTexture.SetExtent(m_swapchain.Extent());
    depthTexture.Create();
    TextureManager::depth.emplace_back(std::move(depthTexture));
}

void Lumine::VulkanBase::createUniform()
{
    UniformBuffer uniformBuffer;
    uniformBuffer.Create(0);
    BufferManager::uniformBuffers = std::move(uniformBuffer);
}


Lumine::VulkanBase::VulkanBase()
{
    createWindow();
    initVulkan();
}

Lumine::VulkanBase::~VulkanBase()
{
    cleanup();
}

void Lumine::VulkanBase::mainLoop()
{
    while (!glfwWindowShouldClose(m_window))
    {
        glfwPollEvents();
        vk::Result res = m_renderer.drawFrame();
        if(res == vk::Result::eErrorOutOfDateKHR || framebufferResized){
            m_swapchain.recreate(m_window);
            framebufferResized = false;
            createDepthResources();
        }
    }
    LumineContext::Get().Device().waitIdle();
}

void Lumine::VulkanBase::framebufferResizeCallback(GLFWwindow *window, int width, int height)
{
    auto app = reinterpret_cast<VulkanBase*>(glfwGetWindowUserPointer(window));
    if (width == 0 || height == 0) {
        return;
    }
    app->framebufferResized = true;
}


